#-*- coding: utf-8 -*-

__all__ = [
  'csv_to_datum',
  'csv_to_list',
  'datum_to_csv',
  'up_datum_from_csv',
  'format_pylot_csv_value_list',
  ]

import sys
import cStringIO #used for CSV string handling
import warnings

from traits.api import implements

from _pylot.minimal.utils import (check_type, unicode2, uprint)
from _pylot.minimal.datum_natures import BaseList
from _pylot.minimal.datum import Datum
from _pylot.data.fileformat import (DialectPyLotCSV, DialectPyLotList)
from _pylot.minimal.csvtools import (UnicodeWriter, unicode_csv_reader)

def convert_value_from_csv(val, nat, datum_type=Datum):
  try :
    return nat.FromCsv(val)
  except AttributeError :
    return datum_type.staticConvert(val, nat)

def format_pylot_csv_value_list(strlist):
  u"""
  >>> csvlist = u'[1, 2],[3, 4]'
  >>> format_pylot_csv_value_list(csvlist)
  [u'[1, 2]', u'[3, 4]']
  >>> csvlist = u'[[1, 2],[3, 4]],[a, b]'
  >>> format_pylot_csv_value_list(csvlist)
  [u'[[1, 2],[3, 4]]', u'[a, b]']
  """
  quotechar = u"'"
  new_list = []
  list_level = 0
  quoting = 0
  var = u''
  for c in strlist :
    if c == u'[' and quoting % 2 == 0 :
      list_level += 1
    elif c == u']' and quoting % 2 == 0 :
      list_level -= 1
      if list_level == 0 :
        var += c
    elif c == quotechar :
      quoting += 1
    if list_level > 0 :
      var += c
    else :
      if var.strip():
        new_list.append(var)
      var = u''

  return new_list

def csv_to_list(src, datum_type=Datum):
  u"""
  Makes a list from CSV string

  >>> d = Datum(u'a', int, 1)
  >>> csv_to_list(u'a;int;1;2;[3,4]')
  [u'a', <type 'int'>, 1, 2, [3, 4], None, None, None]

  >>> d = Datum(tooltip=u'<-- "special char : ; !" -->')
  >>> lst = d.toCsv()
  >>> print csv_to_list(lst)[5]
  <-- "special char : ; !" -->
  """
  check_type(u'src', src, unicode)

  # l = [None, None, None, ...]
  l = [None for attr in datum_type.ordered_keys]

  # transform csv line to list (;int;3 -> [None, 'int', 3, ...])
  reader = unicode_csv_reader([src], dialect=DialectPyLotCSV())
  for row in reader :
    csvl = [field.strip() for field in row]

  for i in range(min(len(csvl), len(l))):
    val = csvl[i]
    if val :
      l[i] = val

  # transform str type into python type ('int' -> <type 'int'>)
  l[1] = datum_type.getType(l[1])

  if l[1] in datum_type.deprecated_nature :
    uprint(u'Deprecated since 2010-04-02, use PyLotList, StrList or Vector instead of SimpleStringList or StringList',
           file=sys.stderr)
    uprint(u'Datum/Parameter %s ignored' % l[0], file=sys.stderr)
  elif l[1] in datum_type.valid_nature :
    #convert values to new type
    if l[2] or l[1] == unicode :
      l[2] = convert_value_from_csv(l[2], l[1], datum_type) #convert value
    if l[3] or l[1] == unicode :
      l[3] = convert_value_from_csv(l[3], l[1], datum_type) #convert default_value

    # transform 5th value (value_list) to list
    # 5th element is like a csv separated by ','
    if l[4] :
      # remove optionnal '[]' delimiters
      # exemple: u"[[1,2],[3.0,4.5]]" -> u"[1,2],[3.0,4.5]"
      str_list = l[4].strip()
      if str_list[0] == u'[' and str_list[-1] == u']' :
        str_list = str_list[1:-1]

      l[4] = []
      if issubclass(l[1], (list, BaseList)) :
        csvlst = format_pylot_csv_value_list(str_list)
      else :
        lst_reader = unicode_csv_reader([str_list],
          dialect=DialectPyLotList())
        for row in lst_reader :
          csvlst = [field.strip() for field in row]

      for val in csvlst :
        if val :
          val = convert_value_from_csv(val, l[1])
          l[4].append(val)
  else :
    # if nature isn't defined in str, values cannot be converted so ...
    l[2] = l[3] = None

  return l

def up_datum_from_csv(datum, line, mod_attrs=None):
  lst = csv_to_list(line, datum.__class__)
  dic = datum.listToDict(lst)
  datum.upFromDict(dic, mod_attrs)

def csv_to_datum(src, datum_type=Datum):
  datum = datum_type()
  up_datum_from_csv(datum, src)
  return datum

def datum_to_csv(datum):
  u"""
  Exports datum to CSV string. All characters, including CSV delimiters are
  allowed in strings.

  >>> print datum_to_csv(Datum(u'd', int, 1, 2, [3,4], tooltip=u"yes", group=u'grooooup'))
  d;int;1;2;[1,2,3,4];yes;grooooup;

  >>> tt = u"'tooltip' with ; and , 'utf8' : 'æ ð' "
  >>> print datum_to_csv(Datum(u'd', int, 1, 2, [3,4], tooltip=tt))
  d;int;1;2;[1,2,3,4];"'tooltip' with ; and , 'utf8' : 'æ ð' ";;

  >>> print datum_to_csv(Datum())
  ;;;;;;;

  :return: CSV string
  :rtype: :obj:`unicode`
  """
  output = cStringIO.StringIO()
  writer = UnicodeWriter(output, dialect=DialectPyLotCSV())
  writer.writerow(datum_to_csv_list(datum))
  csvstr = output.getvalue().decode(u'utf-8')
  output.close()
  return csvstr.strip()

def datum_to_csv_list(datum):
  try : nature = datum._nature.__name__
  except AttributeError : nature = u''

  if nature == u'unicode' :
    nature = u'str'
  elif nature in (u'SimpleStringList', u'StringList', u'list') :
    warnings.warn('Deprecated since 2010-04-02, use PyLotList, StrList or Vector instead of SimpleStringList or StringList', DeprecationWarning)
    nature = u'PyLotList'

  if not datum._value_list :
    value_list = u''
  else :
    vlist = [datum.valuetoCsvValue(elem) for elem in datum._value_list]
    value_list = u'[%s]' % u','.join(vlist)

  if datum.name is None:
    name = u''
  else:
    name = datum.name
  if datum.value is None:
    value = u''
  else:
    value = datum.valuetoCsvValue(datum.value)
  if datum.default_value is None:
    default_value = u''
  else:
    default_value = datum.valuetoCsvValue(datum.default_value)

  str_list = [
  name,
  nature,
  value,
  default_value,
  value_list,
  ]

  for i in range(5, len(datum.ordered_keys)) :
    attr = datum.ordered_keys[i]
    str_list.append(unicode2(getattr(datum, attr)))

  return str_list


if __name__ == '__main__' :
  pass
